#include "crossbit/common.h"
#include "fcntl.h"
#include "signal.h"
#include "sys/time.h"
#include "sys/wait.h"
#include "sys/resource.h"
#include "crossbit/Interpreter.h"
#include "crossbit/MemoryImage.h"
#include "crossbit/ELFMemoryImage.h"
#include "crossbit/SYSCALL.h"
#include "string.h"

using namespace crossbit;


// Implementation related header info
#include "arch/mips/MIPSSyscallHandler.h"
#include "MIPSSyscallInternal.h"

	MIPSSyscallHandler::MIPSSyscallHandler(ProcessorState *state, MemoryImage *img)
: SyscallHandler(state, img)
{
}

MIPSSyscallHandler::~MIPSSyscallHandler()
{
}

XTInt32 MIPSSyscallHandler::syscode()
{
	return (XTInt32)(*(XTInt32*)sh_state->get(2));
}

// Emulate SimpleScalar SStrix syscall
void MIPSSyscallHandler::operator () ()
{
	/* address of the first element of register file */
	XTInt32 *r = (XTInt32 *)sh_state->get(0); 
	XTInt32 syscode = (XTInt32)r[2];

	switch(syscode) {

		case  MIPS_SYS_fork :
			{
				r[2] = SYSCALL::XTFork();
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_read:
			{
				r[2] = SYSCALL::XTRead((XTInt32)r[4], /*fd*/
						(XTInt8 *)(XTMemAddr)r[5], /*buf*/
						(XTInt32)r[6] /*nbyte*/);
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_write:
			{
				r[2] = SYSCALL::XTWrite((XTInt32)r[4], /*fd*/
						(XTInt8 *)((XTMemAddr)r[5]), /*buf*/
						(XTInt32)r[6] /*nbyte*/);
				if (r[2] == r[6])
					r[7] = 0;
				else {
					r[7] = 1;
				}	

				break;
			}
		case MIPS_SYS_open:
			{
				XTInt32 mips_flags = (XTInt32)r[5],
						local_flags = 0;

				for( XTInt32 i = 0 ; i < 32 ; i ++ ) {
					if( mips_flags & ( 1 << i ) ){
						XTInt32 j = 0 ;
						for( ; j < MIPS_NFLAGS ; j++ ) {
							if( ( 1 << i ) & mips_flag_table[j].mips_flag ) {
								local_flags |= mips_flag_table[j].local_flag;
								break;
							}
						}
						assert( j < MIPS_NFLAGS && "XTERROR\n");

					}
				}


				/* open the file */
				/*fd*/ r[2] = SYSCALL::XTOpen((char *)r[4], /*fname*/
						local_flags,
						(mode_t)r[6] /*mode*/);
				//	printf("file open : %s fd = %x\n" , (char*)r[4], r[2] );

				/* check for an error condition */
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_close:
			{
				XTInt32 fd = r[4];
				// don't close stdin, stdout, or stderr
				if (fd == 0 || fd == 1 || fd == 2)
				{
					r[7] = 0;
					break;
				}

				// close the file
				r[2] = SYSCALL::XTClose(fd);

				// check for an error condition
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}


		case MIPS_SYS_execve:
			{
				char* filename = (char*)r[4];
				char** p = (char**)r[5];

				//execve(filename, p, environ);
				if (strcmp(filename, "/bin/sh") == 0)
				{
					char* arg0 = (char*)(p[2]);
					char buf[1024];

					strcpy(buf, "/home/stt/v27/trunk/prog/mips2x86 ");
					strcat(buf, arg0);

					char* cp[] = {"sh", "-c", buf, NULL};
					r[2] = execve("/bin/sh", cp, environ);
				}
				else
				{

					r[2] =	execve("/bin/echo", p, environ);
				}

				// check for an error condition
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_getuid:
			{
				// get user id
				r[2] = SYSCALL::XTGetuid();

				//check for an erro conditon
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_brk:
			{
				r[2] = (XTInt32)SYSCALL::XTBrk((void*)r[4]);	//added by zhengjuyu
				assert( r[4] < r[29] && "XTERROR sys_brk\n");

				if ((signed)r[2] != -1 )
				{
					r[7] = 0;
				}
				else {
					r[2] = -1;   // WARN: errno might be inaccurate, dependent on 
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_wait4:
			{
				r[2] = wait4( r[4] , ( int * ) r[5] , r[6] , (struct rusage * ) r[7] );
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;

			}
		case MIPS_SYS_getgid:
			{
				r[2] = SYSCALL::XTGetgid();

				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_geteuid:
			{
				// get effective user id
				r[2] = SYSCALL::XTGeteuid();

				//check for an erro conditon
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_getegid:
			{
				r[2] = SYSCALL::XTGetegid();

				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_ioctl:
			{
				XTInt8 buf[NUM_IOCTL_BYTES];

				/* if arg ptr exists, copy NUM_IOCTL_BYTES bytes to host mem */
				if (r[6] != 0)
					UTIL::XTMemcpy((void*)buf, (void *)(XTMemAddr)r[6], NUM_IOCTL_BYTES);

				/* perform the ioctl() call */
				r[2] = ioctl(/*fd*/r[4], r[5], buf);

				/* if arg ptr exists, copy NUM_IOCTL_BYTES bytes from host mem */
				if (r[6] != 0)
					UTIL::XTMemcpy((void *)(XTMemAddr)r[6], buf, NUM_IOCTL_BYTES);

				/* check for an error condition */
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else
				{	
					/* got an error, return details */
					r[2] = errno;
					r[7] = 1;
				}
			}
			break;

		case MIPS_SYS_setrlimit:
			{
				struct rlimit *rl;

				rl = (struct rlimit*)(XTMemAddr)r[5];

				r[2] = SYSCALL::XTSetrlimit( (XTInt32)r[4], rl);

				if ( (XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 0;
				}

				break;

			}

		case MIPS_SYS_getrlimit:
			{
				struct rlimit *rl;	

				rl = (struct rlimit *)(XTMemAddr)r[5];

				r[2] = SYSCALL::XTGetrlimit( (XTInt32)r[4], // resource
						rl);	 // rlim

				if ( (XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 0;
				}

				break;
			}

		case MIPS_SYS_getrusage:
			{
				struct rusage local_rusage;

				r[2] = SYSCALL::XTGetrusage( (XTInt32)r[4], &local_rusage);

				if( r[2] != -1 )
					r[7] = 0;
				else
				{
					r[2] = errno;
					r[7] = 1;
				}

				UTIL::XTMemcpy((void*)r[5], &local_rusage, sizeof(struct rusage));

				break;

			}

		case MIPS_SYS_mmap:
			{
				XTInt32 fd = *(XTInt32*)(r[29]+16);
				XTInt32 offset = *(XTInt32*)(r[29] + 20);
				XTInt32 flag = 0;
				XTInt32 prot = 0;
				for( XTInt32 i = 0 ; i < 32 ; i ++ ) {
					if( r[6] & ( 1 << i ) ){
						XTInt32 j = 0 ;
						for( ; j < MMAP_PROT_COUNT ; j++ ) {
							if( ( 1 << i ) & mips_mmap_prot_table[j].mips_flag ) {
								prot |= mips_mmap_prot_table[j].local_flag;
								break;
							}
						}
						assert( j < MMAP_PROT_COUNT && "XTERROR\n");

					}
				}

				for( XTInt32 i = 0 ; i < 32 ; i ++ ) {
					if( r[7] & ( 1 << i )) {
						XTInt32 j = 0 ;
						for( ; j < MMAP_FLAG_COUNT ; j++ ) {
							if(  ( 1 << i ) & mips_mmap_flag_table[j].mips_flag ) {
								flag |= mips_mmap_flag_table[j].local_flag;
								break;
							}
						}
						assert( j < MMAP_FLAG_COUNT && "XTERROR\n");
					}
				}


				//	printf("flag = %x prot = %x\n" , flag , prot );
				r[2] = (XTInt32)SYSCALL::XTMmap( (void*)r[4], r[5], prot, flag, fd, offset);

				if( (void*)r[2] != MAP_FAILED)
					r[7] = 0;
				else
					r[7] = 1;
				break;
			}
		case MIPS_SYS_munmap:
			{
				//dummy
				r[2] = SYSCALL::XTMunmap((void*)r[4], r[5]);
				if( r[2] != -1)
					r[7] = 0;
				else
					r[7] = 1;
				break;
			}

		case MIPS_SYS_uname:
			{
				struct utsname* buf = (struct utsname*)(r[4]);
				r[2] = SYSCALL::XTUname( buf );
				r[7] = 0;
				break;
			}

		case MIPS_SYS_unlink:
			{
				r[2] = SYSCALL::XTUnlink((XTPStr)r[4]); // filename

				// check for an error condition
				if ((int)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_readlink:
			{
				XTPStr path = (XTPStr)r[4];	
				XTPStr buf = (XTPStr)r[5];
				size_t bufsiz = (size_t)r[6];

				r[2] = SYSCALL::XTReadlink(path, buf, bufsiz);

				if((XTInt32)r[2] != -1)
				{
					r[7] = 0;
				}
				else 
				{
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_times:
			{
				struct tms* buf = (struct tms*)r[4];
				r[2] = SYSCALL::XTTimes(buf);

				if((XTInt32)r[2] != -1)
					r[7] = 0;
				else
				{
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_time:
			{
				time_t * t = (time_t*)r[4];

				r[2] = SYSCALL::XTTime(t);

				if((int)r[2] != -1)
					r[7] = 0;
				else
				{
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_stat:
			{
				XTPStr file_name = (XTPStr)r[4];
				struct stat * buf = (struct stat*)r[5];
				r[2] = SYSCALL::XTStat(file_name, buf);

				if((int)r[2] != -1)
					r[7] = 0;
				else
				{
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_fstat:
			{
				struct stat * buf = (struct stat*)r[5];
				r[2] = SYSCALL::XTFstat(r[4], buf);

				if ((int)r[2] != -1)
					r[7] = 0;
				else
				{
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_getpid:
			{
				r[2] = SYSCALL::XTGetpid();

				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_rename:
			{
				r[2] = SYSCALL::XTRename((XTPStr)r[4], (XTPStr)r[5]);

				if((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_llseek:
			{
				long res = 0;
				__asm__ volatile ( "int $0x80" \
						: "=a" ( res )\
						: "0" ( 140 ) , "b" ( r[4] ) , "c" ( r[5] ) , "d" ( r[6] ) , "S" ( r[7] ),\
						"D"( *((XTInt32*)(r[29] + 16) ) ) );
				r[2] = res;

				if((int)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}
		case MIPS_SYS_rt_sigaction:
			{
				__asm__ volatile ( "int $0x80" \
						: "=a" ( r[2] )\
						: "0" ( 174  ) , "b" ( r[4] ) , "c" ( r[5] ) , "d" ( r[6] ) , "S" ( r[7] ),\
						"D"( *((XTInt32*)(r[29] + 16) ) ) );

				if((int)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}

				break;
			}

		case MIPS_SYS_fstat64:
			{
				struct stat64 sbuf;

				r[2] = fstat64((XTInt32)r[4], &sbuf);

				// check for error conditon
				if ((XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}

				UTIL::XTMemset( (void *)r[5] , 0 , 104);
				*(XTInt32 *) r[5] = sbuf.st_dev;
				*(XTInt64 *)(r[5] + 16 ) = (XTInt64)sbuf.st_ino; 
				*(XTInt32 *)(r[5] + 24 ) = sbuf.st_mode; 
				*(XTInt32 *)(r[5] + 28 ) = sbuf.st_nlink; 
				*(XTInt32 *)(r[5] + 32 ) = sbuf.st_uid; 
				*(XTInt32 *)(r[5] + 36 ) = sbuf.st_gid; 
				*(XTInt32 *)(r[5] + 40 ) = sbuf.st_rdev; 
				*(XTInt64 *)(r[5] + 56 ) = (XTInt64)sbuf.st_size; 
				*(XTInt32 *)(r[5] + 64 ) = sbuf.st_atim.tv_sec; 
				*(XTInt32 *)(r[5] + 68 ) = sbuf.st_atim.tv_nsec; 
				*(XTInt32 *)(r[5] + 72 ) = sbuf.st_mtim.tv_sec; 
				*(XTInt32 *)(r[5] + 76 ) = sbuf.st_mtim.tv_nsec; 
				*(XTInt32 *)(r[5] + 80 ) = sbuf.st_ctim.tv_sec; 
				*(XTInt32 *)(r[5] + 84 ) = sbuf.st_ctim.tv_nsec; 
				*(XTInt32 *)(r[5] + 88 ) = sbuf.st_blksize; 
				*(XTInt64 *)(r[5] + 96 ) = (XTInt64)sbuf.st_blocks; 
				//	*(struct stat64*)((XTMemAddr)r[5]) = sbuf;
				break;
			}

		case MIPS_SYS_fcntl64:
			{
				if( r[5] == 14 || r[5] >= 23 ) {
					assert(0 && "XTERROR fcntl64 \n");
				}
				__asm__ volatile ( "int $0x80" \
						: "=a" ( r[2] )\
						: "0" ( 221  ) , "b" ( r[4] ) , "c" ( r[5] ) , "d" ( r[6] ) );

				// check for an error condition
				if ( (XTInt32)r[2] != -1)
					r[7] = 0;
				else {
					r[2] = errno;
					r[7] = 1;
				}
				break;
			}

		case MIPS_SYS_dup : {
								 r[2] = dup ( r[4] );
								 if( r[2] != -1 ) {
									 r[7] = 0;
								 } else {
									 r[2] = errno;
									 r[7] = 1;
								 }
								 break;
							 }


		case MIPS_SYS_dup2 : {
								 r[2] = dup2 ( r[4] , r[5] );
								 if( r[2] != -1 ) {
									 r[7] = 0;
								 } else {
									 r[2] = errno;
									 r[7] = 1;
								 }
								 break;
							 }

		case MIPS_SYS_mremap : {
								   long res = 0;
								   __asm__ volatile ( "int $0x80" \
										   : "=a" ( res )\
										   : "0" ( 163 ) , "b" ( r[4] ) , "c" ( r[5] ) , "d" ( r[6] ) , "S" ( r[7] ),\
										   "D"( *((XTInt32*)(r[29] + 16) ) ) );
								   r[2] = res;

								   if((int)r[2] != -1)
									   r[7] = 0;
								   else {
									   r[2] = errno;
									   r[7] = 1;
								   }
								   break;
							   }
		case MIPS_SYS_nanosleep  : {
									   r[2] = nanosleep ( ( timespec * ) r[4] , (timespec *) r[5] );
									   if((int)r[2] != -1)
										   r[7] = 0;
									   else {
										   r[2] = errno;
										   r[7] = 1;
									   }
									   break;
								   }

		case MIPS_SYS_rt_sigprocmask : {
										   __asm__ volatile ( "int $0x80" \
												   : "=a" ( r[2] )\
												   : "0" ( 175  ) , "b" ( r[4] ) , "c" ( r[5] ) , "d" ( r[6] ), "S"( r[7]) );
										   if((int)r[2] != -1)
											   r[7] = 0;
										   else {
											   r[2] = errno;
											   r[7] = 1;
										   }
										   break;
									   }

		case MIPS_SYS_pipe:{
							   XTInt32  file[2] = { 0 };
							   r[2] = pipe( file );
							   r[2] = file[0];
							   r[3] = file[1];
							   if( r[2] != -1 ) {
								   r[7] = 0;
							   } else {
								   r[2] = errno;
								   r[7] = 1;
							   }
							   break;

						   }
		case MIPS_SYS_exitgroup:
						   {
							   SYSCALL::XTExit(2);
							   break;
						   }

		//added by Roger
		case MIPS_SYS_sysctl:
		{
				struct __sysctl_args args;
				
				r[2] = SYSCALL::XTSysctl(&args);

				if((XTInt32) r[2] != -1)
						r[7] = 0;
				else{
						r[2] = errno;
						r[7] = 1;
				}
				
				UTIL::XTMemcpy((void*)r[4], &args, sizeof(struct __sysctl_args));
				break;
		}

		default:
						   UTIL::XTLOG("Unknown syscall: 0x%x\n", syscode);
						   assert(0 && "Unrecognized syscall\n");
	}
}
